using System;
using System.Collections.Generic;
using System.Text;
using ET = ErlyThreads;

namespace Testbed
{
    static class NCuncurrentWorkers
    {
        [Serializable]
        class StartParamMessage
        {
            public StartParamMessage(int pIterations)
            {
                mNumberOfIterations = pIterations;
            }

            readonly int mNumberOfIterations;

            public int NumberOfIterations
            {
                get
                {
                    return mNumberOfIterations;
                }
            }
        }

        [Serializable]
        class ProgressMessage
        {
            public ProgressMessage(
                string pID,
                int pProgress)
            {
                ID = pID;
                Progress = pProgress;
            }
            public readonly string ID;
            public readonly int Progress;
        }

        static string gMasterThreadID;

        public static int Run()
        {
            ET.ProcessRuntime master = new ET.ProcessRuntime(
                (gMasterThreadID = Program.NewID()));

            // start ten threads to do the same thing all at the same time
            IDictionary<string, int> idToPosition =
                new Dictionary<string, int>();

            IDictionary<string, bool> doneList =
                new Dictionary<string, bool>();

            // get it up and running
            for (int i = 0 ; i < 200 ; i++) // top limit here is the number of worker threads
            {
                string newID;    
                idToPosition.Add(
                    (newID = Program.NewID()),
                    i);
                doneList[newID] = false;
                master.InitProcess(
                    newID,
                    WorkerStart);
                master.ProcessHandle.SubscribeToMessages(
                    newID,
                    delegate(Type t)
                    {
                        if (t == typeof(ET.Messages.ThreadStopped))
                        {
                            return true;
                        }
                        else if (t == typeof(ProgressMessage))
                        {
                            return true;
                        }

                        return false;
                    });
                master.ProcessHandle.SendMessage(
                    newID,
                    new StartParamMessage(
                        1000)); // this is the number of iterations in each thread
            }

            // start the threads
            foreach (string id in idToPosition.Keys)
            {
                master.StartProcess(id);
            }

            NSnakes screenUpdater = new NSnakes();

            // wait for updates
            bool mustQuit = false;
            while (!mustQuit)
            {
                master.ProcessHandle.WaitForMessages();
                master.ProcessHandle.DispatchQueuedMessages(
                    delegate(ET.ReceivedMessage msg)
                    {
                        if (msg.Message.GetType() ==
                            typeof(ET.Messages.ThreadStopped))
                        {
                            ET.Messages.ThreadStopped stopMSG =
                                msg.Message as ET.Messages.ThreadStopped;
                            doneList[stopMSG.Id as string] = true;
                        }
                        else if (msg.Message.GetType() ==
                            typeof(ProgressMessage))
                        {
                            ProgressMessage updateMSG =
                                msg.Message as ProgressMessage;
                            screenUpdater.SetSnakeValue(
                                idToPosition[updateMSG.ID],
                                updateMSG.Progress);
                        }
                    });
                //screenUpdater.Update();

                mustQuit = true;
                foreach (string s in doneList.Keys)
                {
                    mustQuit &= doneList[s];
                    if (!mustQuit)
                    {
                        break;
                    }
                }
            }

            return Program.SUCCESS;
        }

        static void WorkerStart(ET.IProcessHandle pThreadContainer)
        {
            pThreadContainer.WaitForMessages();
            int numberIterations = 0;

            pThreadContainer.DispatchQueuedMessages(
                delegate(ET.ReceivedMessage initialMessage)
                {
                    StartParamMessage p = initialMessage.Message as StartParamMessage;

                    if (p == null)
                    {
                        throw new ApplicationException(
                            "Received the wrong type of message");
                    }

                    numberIterations = p.NumberOfIterations;
                });

            for (int i = 0 ; i < numberIterations ; i++)
            {
                // send the progress report
                pThreadContainer.SendMessage(
                    gMasterThreadID,
                    new ProgressMessage(
                        pThreadContainer.Id as string,
                        i));
            }
        }
    }
}
